import inspect
import json
from abc import abstractmethod, ABC
from dataclasses import dataclass
from typing import Generic, Any, final, Tuple
from aiohttp import web

from ..jsonpat import from_json, to_json, JsonVerificationError
from ..logger import AbstractLogger
from .request_handler import HttpEndpointDescriptor, ContextType
from .errors import BusinessError


@dataclass
class JsonRequestExtraData:
    request: web.Request
    body: str


class JsonEndpointDescriptor(HttpEndpointDescriptor[ContextType], ABC, Generic[ContextType]):
    @final
    async def on_request(self, *, context: ContextType, logger: AbstractLogger, request: web.Request):
        input_body_type, input_query_type, output_type = self.types

        req_text = await request.text()
        if len(req_text) == 0:
            req_json = None
        else:
            try:
                req_json = json.loads(req_text)
            except Exception as e:
                logger.warn(f"无法解析request body为json，{e}")
                return web.Response(status=400)  # 400 - bad request

        query_dict = {}
        for query_key, query_value in request.query.items():
            if query_key not in query_dict:
                query_dict[query_key] = []
            query_dict[query_key].append(query_value)

        logger.debug(f"调用 {request.url} 参数为  '{req_text[:400]}'")

        try:
            req_body = from_json(req_json, input_body_type)
            req_query = from_json(query_dict, input_query_type)
        except JsonVerificationError as e:
            raise BusinessError("format_error", str(e))

        extra_data = JsonRequestExtraData(request=request, body=req_text)
        result = await self.on_json_request(query=req_query, body=req_body, context=context, logger=logger, extra_data=extra_data)

        if output_type is web.Response:
            if not isinstance(result, web.Response):
                raise RuntimeError("输出格式错误")
            return result
        else:
            try:
                result_json = to_json(result, output_type)
            except JsonVerificationError as e:
                logger.error(f"输出格式错误 {e.value} {e}, 输出类型为: {type(result)}")
                raise RuntimeError("输出格式错误")

            resp_body = json.dumps({"result": result_json}, ensure_ascii=False, separators=(",", ":"))

            logger.debug(f"调用 [{self.method}] {request.url} 成功，结果为 '{resp_body[:400]}'")
            return web.Response(body=resp_body, content_type="application/json")

    @property
    def types(self) -> Tuple[Any, Any, Any]:
        specs = inspect.getfullargspec(self.on_json_request)
        return_type = specs.annotations.get("return", Any)
        input_body_type = specs.annotations.get('body', Any)
        input_query_type = specs.annotations.get('query', Any)
        return input_body_type, input_query_type, return_type

    @abstractmethod
    async def on_json_request(self, *, query: Any, body: Any, context: ContextType, logger: AbstractLogger, extra_data: JsonRequestExtraData):
        pass

